package ice.server.comm;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.NumberUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 
 * @author zhangrb
 *
 * @param <T>
 */
public class CommSpecification<T> implements Specification<T> {
	
	private static final long serialVersionUID = 2731223265075098819L;

	private static DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	private Map<String, Object> pm;
	
	private CommSpecification() {
		
	}
	
	public static <T> CommSpecification<T> product(Map<String, Object> pm) {
		CommSpecification<T> cs =  new CommSpecification<T>();
		cs.pm = pm;
		return cs;
	}
	
	private String cutSubfix(String p) {
		String reg = "(" + Constants.SW_SF + "|" + Constants.EW_SF + "|" + Constants.CONS_SF + "|" + Constants.IN_SF + "|" + 
				Constants.GT_SF + "|" + Constants.LT_SF + "|" + Constants.GE_SF + "|" + Constants.LE_SF + "|" + Constants.NE_SF + "|" + 
				Constants.NOTIN_SF + "|" + Constants.BT_SF + "|" + Constants.EMPTY_SF + "|" +  Constants.NOTEMPTY_SF + ")$";
		return p.replaceAll(reg, "");
	}

	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
		List<Predicate> predicate = new ArrayList<Predicate>();
		if (pm != null) {
			for (Map.Entry<String, Object> p : pm.entrySet()) {
				String pro = cutSubfix(p.getKey());
				String val = p.getValue()==null?null:p.getValue().toString();
				if(StringUtils.isEmpty(pro) || val == null) {
					continue;
				}
				if(pro.equals(Constants.PAGE) || pro.equals(Constants.ROWS) || pro.startsWith(Constants.ORDERBY)) {
					continue;
				}
				Class<?> c = root.getJavaType();
				if(c != null) {
					if(ReflectionUtils.findField(c, pro) == null) {
						// 属性不属于po，则忽略掉
						continue;
					}
				}
				if(StringUtils.isEmpty(val) && !p.getKey().endsWith(Constants.EMPTY_SF) && !p.getKey().endsWith(Constants.NOTEMPTY_SF)) {
					// 如果value为空，并且key不是empty或notempty结尾，就罗忽略
					continue;
				}

				Predicate pc = null;
				if(p.getKey().equals(pro)) {
					//equals
					pc = cb.equal(root.get(pro), val);
				} else if(p.getKey().endsWith(Constants.SW_SF)) {
					//start with
					pc = cb.like(root.get(pro).as(String.class), val + "%");
				} else if(p.getKey().endsWith(Constants.EW_SF)) {
					//end with
					predicate.add(cb.like(root.get(pro).as(String.class), "%" + val));
				} else if(p.getKey().endsWith(Constants.CONS_SF)) {
					//contains
					predicate.add(cb.like(root.get(pro).as(String.class), "%" + val + "%"));
				} else if(p.getKey().endsWith(Constants.IN_SF)) {
					pc =  new In_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.GT_SF)) {
					//大于
					pc =  new Gt_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.LT_SF)) {
					//小于
					pc =  new Lt_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.LE_SF)) {
					//小于等于
					pc =  new Le_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.GE_SF)) {
					//大于等于
					pc =  new Ge_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.NE_SF)) {
					pc =  new Ne_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.NOTIN_SF)) {
					//not in 必须是以逗号隔开的字符串
					pc =  new Notin_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.BT_SF)) {
					//between and ,两个值之间以，分割
					pc =  new BwAnd_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.EMPTY_SF)) {
					pc = new  Empty_filter().provide(root, cb, pro, val);
				} else if(p.getKey().endsWith(Constants.NOTEMPTY_SF)) {
					pc = new  NotEmpty_filter().provide(root, cb, pro, val);
				}
				if(pc != null) {
					predicate.add(pc);
				}
			}
		}
        Predicate[] pre = new Predicate[predicate.size()];
        return query.where(predicate.toArray(pre)).getRestriction();
	}
	
	private static interface Filter {
		public Predicate provide(Root<?> root, CriteriaBuilder cb, String pro, String val);
	}

	private static class In_filter implements Filter {
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//in 语句, 必须是以逗号隔开的字符串
			String[] vals = StringUtils.delimitedListToStringArray(val, ",");
			List<?> valList = null;
			Class<?> c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				ArrayList<Date> tt = new ArrayList<Date>();
				for (String v : vals) {
					try {
						tt.add(df.parse(v));
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
				valList = tt;
			} else {
				valList = Arrays.asList(vals);
			}
			if(valList.size() < 1) {
				return null;
			}
			return root.get(pro).in(valList);
		}
	}
	
	private static class Gt_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.greaterThan(root.get(pro).as(c), o);
		}
	}
	
	private static class Lt_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.lessThan(root.get(pro).as(c), o);
		}
	}
	
	private static class Le_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.lessThanOrEqualTo(root.get(pro).as(c), o);
		}
	}
	
	private static class Ge_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.greaterThanOrEqualTo(root.get(pro).as(c), o);
		}
	}
	
	private static class Ne_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.notEqual(root.get(pro).as(c), o);
		}
	}

	private static class Empty_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.or(cb.isNull(root.get(pro)), cb.equal(root.get(pro).as(c), ""));
		}
	}

	private static class NotEmpty_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Comparable o = null;
			Class c = root.get(pro).getJavaType();
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				try {
					o = df.parse(val);
				} catch (ParseException e) {
					e.printStackTrace();
				}
			} else {
				o = val;
			}
			return cb.and(cb.isNotNull(root.get(pro)), cb.notEqual(root.get(pro).as(c), ""));
		}
	}
	
	private static class Notin_filter implements Filter {

		@SuppressWarnings({ "rawtypes"})
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Class c = root.get(pro).getJavaType();
			String[] vals = StringUtils.delimitedListToStringArray(val, ",");
			List<?> valList = null;
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				ArrayList<Date> tt = new ArrayList<Date>();
				for (String v : vals) {
					try {
						tt.add(df.parse(v));
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
				valList = tt;
			} else {
				valList = Arrays.asList(vals);
			}
			if(valList.size() < 1) {
				return null;
			}
			return root.get(pro).in(valList).not();
		}
	}
	
	private static class BwAnd_filter implements Filter {

		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Predicate provide(Root<?> root,CriteriaBuilder cb, String pro, String val) {
			//大于
			Class c = root.get(pro).getJavaType();
			String[] vals = StringUtils.delimitedListToStringArray(val, ",");
			List valList = null;
			if(Date.class.isAssignableFrom(c)) {
				//日期类型
				ArrayList<Date> tt = new ArrayList<Date>();
				for (String v : vals) {
					try {
						tt.add(df.parse(v));
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
				valList = tt;
			} else if(Number.class.isAssignableFrom(c)|| Byte.TYPE.equals(c)
					|| Short.TYPE.equals(c)
					|| Integer.TYPE.equals(c)
					|| Long.TYPE.equals(c)
					|| Float.TYPE.equals(c)
					|| Double.TYPE.equals(c)) {
				ArrayList<Number> tt = new ArrayList<Number>();
				for (String v : vals) {
					tt.add(NumberUtils.parseNumber(v, Number.class));
				}
				valList = tt;
			} else {
				valList = Arrays.asList(vals);
			}
			if(valList.size() < 2) {
				return null;
			}
			return cb.between(root.get(pro).as(c), (Comparable)valList.get(0), (Comparable)valList.get(1));
		}
	}
}
